﻿
using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Micro.Core.FrameWork.ORM.Dapper;
using Microsoft.Extensions.Configuration;

namespace Micro.Core.FrameWork.ORM.Dapper
{
    public class SqlDapper : ISqlDapper
    {
        private readonly IConfiguration _configuration;
        public SqlDapper(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        private IDbConnection _connection { get; set; }

        public IDbConnection Connection
        {
            get
            {
                return _connection = new SqlConnection(_configuration.GetSection("Con").Value);
            }
        }

        /// <typeparam name="T"></typeparam>
        /// <param name="cmd"></param>
        /// <param name="param"></param>
        /// <param name="commandType"></param>
        /// <param name="beginTransaction"></param>
        /// <returns></returns>
        public List<T> QueryList<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class
        {
            return Execute((conn, dbTransaction) => conn.Query<T>(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text).ToList(), beginTransaction);
        }
        /// <typeparam name="T"></typeparam>
        /// <param name="cmd"></param>
        /// <param name="param"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public async Task<List<T>> QueryListAsync<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class
        {
            return await Execute(async (conn, dbTransaction) => (await conn.QueryAsync<T>(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text)).ToList(), beginTransaction);
        }
        public T QueryFirst<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class
        {
            List<T> list = QueryList<T>(cmd, param, commandType: commandType ?? CommandType.Text, beginTransaction: beginTransaction).ToList();
            return list.Count == 0 ? null : list[0];
        }
        public async Task<T> QueryFirstAsync<T>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class
        {
            List<T> list = (await QueryListAsync<T>(cmd, param, commandType: commandType ?? CommandType.Text, beginTransaction: beginTransaction)).ToList();
            return list.Count == 0 ? null : list[0];
        }
        public object ExecuteScalar(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false)
        {
            return Execute<object>((conn, dbTransaction) => conn.ExecuteScalar(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text), beginTransaction);
        }

        public async Task<int> ExcuteNonQueryAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false)
        {
            return await Execute<Task<int>>(async (conn, dbTransaction) => await conn.ExecuteAsync(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text), beginTransaction);
        }
        public int ExcuteNonQuery(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false)
        {
            return Execute<int>((conn, dbTransaction) => conn.Execute(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text), beginTransaction);
        }
        public IDataReader ExecuteReader(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) =>
            Execute<IDataReader>((conn, dbTransaction) => conn.ExecuteReader(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text), beginTransaction, false);

        public SqlMapper.GridReader QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false)
        {
            return Execute((conn, dbTransaction) => conn.QueryMultiple(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text), beginTransaction, false);
        }

        /// <summary>
        /// 获取output值 ("@b");
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="cmd"></param>
        /// <param name="param"></param>
        /// <param name="commandType"></param>
        /// <param name="beginTransaction"></param>
        /// <returns></returns>
        public (List<T1>, List<T2>) QueryMultiple<T1, T2>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false)
        {
            using SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType, beginTransaction);
            return (reader.Read<T1>().ToList(), reader.Read<T2>().ToList());
        }

        public (List<T1>, List<T2>, List<T3>) QueryMultiple<T1, T2, T3>(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false)
        {
            using SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType, beginTransaction);
            return (reader.Read<T1>().ToList(), reader.Read<T2>().ToList(), reader.Read<T3>().ToList());
        }


        private T Execute<T>(Func<IDbConnection, IDbTransaction, T> func, bool beginTransaction = false, bool disposeConn = true)
        {
            IDbTransaction dbTransaction = null;
            if (beginTransaction)
            {
                Connection.Open();
                dbTransaction = Connection.BeginTransaction();
            }
            try
            {
                T resultT = func(Connection, dbTransaction);
                dbTransaction?.Commit();
                return resultT;
            }
            catch (Exception)
            {
                dbTransaction?.Rollback();
                Connection.Dispose();
                throw;
            }
            finally
            {
                if (disposeConn)
                {
                    Connection.Dispose();
                }
            }
        }
        public bool Add<T>(T entity)
        {
            return AddRange<T>(new T[] { entity });
        }
        public bool AddRange<T>(IEnumerable<T> entities)
        {
            return true;
        }
        public bool Update<T>(T entity, Expression<Func<T, object>> update = null)
        {
            return UpdateRange<T>(new T[] { entity }, update);
        }
        public bool UpdateRange<T>(IEnumerable<T> entities, Expression<Func<T, object>> updateFileds = null)
        {
            return true;
        }

        public bool DelByKey<T>(params object[] keys)
        {
            return true;
        }


    }
}
